* @error: return location for error, or %NULL
* @Varargs: list of key-value save options
*
- * Saves pixbuf to a file in format @type. By default, "jpeg", "png" and
- * "ico" are possible file formats to save in, but more formats may be
+ * Saves pixbuf to a file in format @type. By default, "jpeg", "png", "ico"
+ * and "bmp" are possible file formats to save in, but more formats may be
* installed. The list of all writable formats can be determined in the
* following way:
*
* @option_values: values for named options
* @error: return location for error, or %NULL
*
- * Saves pixbuf to a file in @type, which is currently "jpeg", "png" or "ico".
+ * Saves pixbuf to a file in @type, which is currently "jpeg", "png", "ico" or "bmp".
* If @error is set, %FALSE will be returned.
* See gdk_pixbuf_save () for more details.
*
* @error: return location for error, or %NULL
*
* Saves pixbuf to a callback in format @type, which is currently "jpeg",
- * "png" or "ico". If @error is set, %FALSE will be returned. See
+ * "png", "ico" or "bmp". If @error is set, %FALSE will be returned. See
* gdk_pixbuf_save_to_callback () for more details.
*
* Return value: whether an error was set
* @Varargs: list of key-value save options
*
* Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
- * "png" or "ico". This is a convenience function that uses
+ * "png", "ico" or "bmp". This is a convenience function that uses
* gdk_pixbuf_save_to_callback() to do the real work. Note that the buffer
* is not nul-terminated and may contain embedded nuls.
* If @error is set, %FALSE will be returned and @string will be set to
* @error: return location for error, or %NULL
*
* Saves pixbuf to a new buffer in format @type, which is currently "jpeg",
- * "png" or "ico". See gdk_pixbuf_save_to_buffer() for more details.
+ * "png", "ico" or "bmp". See gdk_pixbuf_save_to_buffer() for more details.
*
* Return value: whether an error was set
*
return TRUE;
}
+/* for our convenience when filling file header */
+#define put16u(buf,data) { *(guint16*)(buf) = GUINT16_TO_LE (data); buf += 2; }
+#define put32u(buf,data) { *(guint32*)(buf) = GUINT32_TO_LE (data); buf += 4; }
+#define put16(buf,data) { *(gint16*)(buf) = GINT16_TO_LE (data); buf += 2; }
+#define put32(buf,data) { *(gint32*)(buf) = GINT32_TO_LE (data); buf += 4; }
+
+static gboolean
+gdk_pixbuf__bmp_image_save_to_callback (GdkPixbufSaveFunc save_func,
+ gpointer user_data,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ guint width, height, channel, size, stride, src_stride, x, y;
+ guchar BFH_BIH[40], *pixels, *buf, *src, *dst, *dst_line;
+ gboolean ret;
+
+ width = gdk_pixbuf_get_width (pixbuf);
+ height = gdk_pixbuf_get_height (pixbuf);
+ channel = gdk_pixbuf_get_n_channels (pixbuf);
+ pixels = gdk_pixbuf_get_pixels (pixbuf);
+ src_stride = gdk_pixbuf_get_rowstride (pixbuf);
+ stride = (width * 3 + 3) & ~3;
+ size = stride * height;
+
+ /* filling BFH */
+ dst = BFH_BIH;
+ *dst++ = 'B'; /* bfType */
+ *dst++ = 'M';
+ put32u (dst, size + 14 + 40); /* bfSize */
+ put32u (dst, 0); /* bfReserved1 + bfReserved2 */
+ put32u (dst, 14 + 40); /* bfOffBits */
+
+ /* filling BIH */
+ put32u (dst, 40); /* biSize */
+ put32 (dst, width); /* biWidth */
+ put32 (dst, height); /* biHeight */
+ put16 (dst, 1); /* biPlanes */
+ put16 (dst, 24); /* biBitCount */
+ put32u (dst, BI_RGB); /* biCompression */
+ put32u (dst, size); /* biSizeImage */
+ put32 (dst, 0); /* biXPelsPerMeter */
+ put32 (dst, 0); /* biYPelsPerMeter */
+ put32u (dst, 0); /* biClrUsed */
+ put32u (dst, 0); /* biClrImportant */
+
+ if (!save_func (BFH_BIH, 14 + 40, error, user_data))
+ return FALSE;
+
+ dst_line = buf = g_try_malloc (size);
+ if (!buf) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_INSUFFICIENT_MEMORY,
+ _("Couldn't allocate memory for saving BMP file"));
+ return FALSE;
+ }
+
+ /* saving as a bottom-up bmp */
+ pixels += (height - 1) * src_stride;
+ for (y = 0; y < height; ++y, pixels -= src_stride, dst_line += stride) {
+ dst = dst_line;
+ src = pixels;
+ for (x = 0; x < width; ++x, dst += 3, src += channel) {
+ dst[0] = src[2];
+ dst[1] = src[1];
+ dst[2] = src[0];
+ }
+ }
+ ret = save_func (buf, size, error, user_data);
+ g_free (buf);
+
+ return ret;
+}
+
+static gboolean
+save_to_file_cb (const gchar *buf,
+ gsize count,
+ GError **error,
+ gpointer data)
+{
+ gint bytes;
+
+ while (count > 0) {
+ bytes = fwrite (buf, sizeof (gchar), count, (FILE *) data);
+ if (bytes <= 0)
+ break;
+ count -= bytes;
+ buf += bytes;
+ }
+
+ if (count) {
+ g_set_error (error,
+ GDK_PIXBUF_ERROR,
+ GDK_PIXBUF_ERROR_FAILED,
+ _("Couldn't write to BMP file"));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdk_pixbuf__bmp_image_save (FILE *f,
+ GdkPixbuf *pixbuf,
+ gchar **keys,
+ gchar **values,
+ GError **error)
+{
+ return gdk_pixbuf__bmp_image_save_to_callback (save_to_file_cb,
+ f, pixbuf, keys,
+ values, error);
+}
+
void
MODULE_ENTRY (bmp, fill_vtable) (GdkPixbufModule *module)
{
module->begin_load = gdk_pixbuf__bmp_image_begin_load;
module->stop_load = gdk_pixbuf__bmp_image_stop_load;
module->load_increment = gdk_pixbuf__bmp_image_load_increment;
+ module->save = gdk_pixbuf__bmp_image_save;
+ module->save_to_callback = gdk_pixbuf__bmp_image_save_to_callback;
}
void
info->description = N_("The BMP image format");
info->mime_types = mime_types;
info->extensions = extensions;
- info->flags = GDK_PIXBUF_FORMAT_THREADSAFE;
+ info->flags = GDK_PIXBUF_FORMAT_WRITABLE | GDK_PIXBUF_FORMAT_THREADSAFE;
info->license = "LGPL";
}